今天就是最後一天啦!今年雖然也是充滿波折,但還是順利完賽
今天來完成剩下的API,首先是登入的API,這邊只實作帶Address和pem檔案做驗證登入,按實際作法是登入後會在server中儲存資料和發給認證(例如JWT token)然後用認證來做後續實作。
首先是要新增一個saved的資料夾存放上傳的檔案,這邊要特別建議如果要真的拿來實際應用的話,需要轉出成json檔案,因為json檔案可以設定密碼,這樣會比較安全。
再來是GetBalance和withdraw,這邊都需要做認證,但我們今天沒有做JWT認證,只單純實作API。
api/routes/api.go
package routes
import "elrondWallet/api/controllers"
func init() {
// unauthorized
unauthorizedAPI := engine.Group("api/v1/wallet")
{
unauthorizedAPI.GET("/mnemonic", controllers.Mnemonic)
unauthorizedAPI.POST("/pem", controllers.DownloadPem)
unauthorizedAPI.POST("/:address/login", controllers.Login)
unauthorizedAPI.GET("/:address/balance", controllers.GatBalance)
unauthorizedAPI.POST("/:address/withdraw", controllers.Withdraw)
}
}
api/controllers/wallet.go
package controllers
import (
walletUtils "elrondWallet/internal/wallet"
"fmt"
"github.com/gin-gonic/gin"
"math"
"net/http"
"path/filepath"
)
func Mnemonic(c *gin.Context) {
mnemonic, _ := walletUtils.GenerateNewMnemonic()
c.JSON(200, gin.H{
"code": "00",
"mnemonic": mnemonic,
})
}
type MnemonicReq struct {
Mnemonic string `json:"mnemonic"`
}
type WithdrawReq struct {
Receiver string `json:"receiver"`
Sender string `json:"sender"`
Amount float64 `json:"amount"`
}
func DownloadPem(c *gin.Context) {
var ap MnemonicReq
if err := c.ShouldBindJSON(&ap); err != nil {
c.JSON(400, gin.H{"error": "Calculator error"})
return
}
privateKey := walletUtils.GetPrivateKeyFromMnemonic(ap.Mnemonic, 0, 0)
address, _ := walletUtils.GetAddressFromPrivateKey(privateKey)
err := walletUtils.SavePrivateKeyToPemFile(privateKey, address)
if err != nil {
return
}
c.Header("Content-Description", "File Transfer")
c.Header("Content-Transfer-Encoding", "binary")
c.Header("Content-Disposition", "attachment; filename="+address+".pem")
c.Header("Content-Type", "application/octet-stream")
c.File(address + ".pem")
}
func Login(c *gin.Context) {
file, _ := c.FormFile("file")
filename := filepath.Base(file.Filename)
fmt.Println("filename", filename)
filePath := "saved/" + filename
if err := c.SaveUploadedFile(file, filePath); err != nil {
// 存檔失敗時的錯誤處理
c.String(http.StatusBadRequest, fmt.Sprintf("upload file err: %s", err.Error()))
return
}
privateKey, _ := walletUtils.LoadPrivateKeyFromPemFile(filePath)
address, _ := walletUtils.GetAddressFromPrivateKey(privateKey)
if address != c.Param("address") {
c.String(http.StatusBadRequest, fmt.Sprintf("File %s address and param address not match.", file.Filename))
return
}
c.JSON(http.StatusOK, gin.H{
"code": "00",
"message": "login success!",
})
}
func GatBalance(c *gin.Context) {
address, _ := getAddressFromReq(c)
balance := walletUtils.GetAddressBalance(address)
c.JSON(http.StatusOK, gin.H{
"code": "00",
"balance": balance,
})
}
func Withdraw(c *gin.Context) {
address, privateKey := getAddressFromReq(c)
var withdrawReq WithdrawReq
if err := c.ShouldBindJSON(&withdrawReq); err != nil {
c.JSON(400, gin.H{"error": "Calculator error"})
return
}
if address != c.Param("address") {
c.String(http.StatusBadRequest, fmt.Sprintf("Json address and param address not match."))
return
}
addressNonce := walletUtils.GetAddressNonce(address)
code := walletUtils.SentTransaction(withdrawReq.Sender, withdrawReq.Receiver, fmt.Sprint(
int(withdrawReq.Amount*(math.Pow(10, 18)))), addressNonce, 1000000000, 50000, privateKey)
c.JSON(http.StatusOK, gin.H{
"code": "00",
"message": code,
})
}
func getAddressFromReq(c *gin.Context) (string, []byte) {
address := c.Param("address")
filePath := "saved/" + address + ".pem"
privateKey, _ := walletUtils.LoadPrivateKeyFromPemFile(filePath)
address, _ = walletUtils.GetAddressFromPrivateKey(privateKey)
return address, privateKey
}
測試結果
Login
Balanace
Sent transaction
鏈上紀錄
以上就是這次鐵人挑戰賽的全部啦,希望各位有從我雜亂的文章中學到點什麼,這次練習的Repo,這次完賽的心得是真的要事先打好稿而且示範的專案最好是事先準備好,有時一卡Bug就真的會難產,希望明年的鐵人賽可以更加的有架構性的去分享給大家。